#ifndef GENERATE_POLICY_IMP_H
#define GENERATE_POLICY_IMP_H

#include <mili/mili.h>
#include "aso-core/therapy.h"
#include "aso-core/generate_policy.h"

/*! Politicas de Generacion de Arboles*/

typedef std::list<ChildId> ListOfNodes;

/*! Implementación de politica de generación a lo ancho */
class Width : public GenerationPolicy
{
    ListOfNodes pending;
    virtual CallbackAction node_creation_callback(const ChildId& cid, Score)
    {
        pending.push_back(cid);             //! lo pongo en lista de pendientes
        return KeepAndContinueWithSibling;  //! indico continuar con el siguiente
    }

    virtual void post_combinations_callback(ChildProcessor* processor)
    {
        while (!pending.empty())
        {
            ChildId aux(pending.front());
            pending.pop_front();           //! remuevo uno de la lista de pendientes

            processor->process_node(aux); //! lo proceso
        }
    }
};

/*! Implementación de politica de generación en profundidad */
class Depth : public GenerationPolicy
{
    virtual CallbackAction node_creation_callback(const ChildId&, Score)
    {
        return ProcessChild; //! indico procesarlo
    }

    virtual void post_combinations_callback(ChildProcessor*)
    {}                       //! como no hay nodos pendientes no hago nada
};

/*! Implementación de politica de generación de los mejores N */
struct ScoredChild
{
    ChildId child;
    Score score;
    ScoredChild(ChildId child, Score score): child(child), score(score)
    {}
    inline bool operator==(const ScoredChild& other) const
    {
        return (child == other.child && score == other.score);
    }
};

struct ScoredCompC
{
    bool operator()(const ScoredChild& t1, const ScoredChild& t2)
    {
        return t1.score > t2.score;
    }
};

typedef mili::Ranker<ScoredChild, mili::AddAfterEqual, ScoredCompC> RankingC;

class BetterBeam : public GenerationPolicy
{
    RankingC beam;
    size_t top;
    virtual CallbackAction node_creation_callback(const ChildId& cid, Score score)
    {
        if (!beam.insert(ScoredChild(cid, score)))
        {
            return DiscardAndContinueWithSibling;
        }
        return KeepAndContinueWithSibling;                 //! indico continuar con el siguiente
    }

    virtual void post_combinations_callback(ChildProcessor* processor)
    {
        while (!beam.empty())
        {
            ChildId aux(beam.top().child);
            beam.remove_first(beam.top(), mili::NoDisposalPolicy);  //! remuevo uno de la lista de pendientes
            processor->process_node(aux);               //! lo proceso
        }
    }

public:
    //! Constructor
    BetterBeam(size_t top): beam(top), top(top)
    {}
};

#endif
